##
#   Project: BlueDUN
#            Internet connection manager for bluetooth devices.
#    Author: Fabio Castelli <muflone@vbsimple.net>
# Copyright: 2009 Fabio Castelli
#   License: GPL-2+
#  This program is free software; you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the Free
#  Software Foundation; either version 2 of the License, or (at your option)
#  any later version.
# 
#  This program is distributed in the hope that it will be useful, but WITHOUT
#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
#  more details.
# 
# On Debian GNU/Linux systems, the full text of the GNU General Public License
# can be found in the file /usr/share/common-licenses/GPL-2.
##

import gtk
import gtk.glade
import pygtk
import os.path
import select
import btsupport
import settings
import gettext
import handlepaths
import services
from gettext import gettext as _

COL_ICON, COL_CLASS, COL_TYPE, COL_DETAIL, COL_NAME, COL_ADDRESS = range(6)
modelDevices = None
tvwDevices = None
toolbRefresh = None
toolbServices = None
btnOk = None
address = None
portDUN = None

# Load domain for translation
for module in (gettext, gtk.glade):
  module.bindtextdomain(handlepaths.APP_NAME, handlepaths.getPath('locale'))
  module.textdomain(handlepaths.APP_NAME)

def addNewDevice(address, name, deviceClass):
  "Add a new device to the list"
  if deviceClass > 0:
    minor_class, major_class, services_class = btsupport.getClasses(deviceClass)
    deviceType = btsupport.getDeviceType(major_class)
    deviceDetail = btsupport.getDeviceDetail(major_class, minor_class)
  else:
    deviceType = (0, 'adapter')
    deviceDetail = (0, _('adapter'))
  iconPath = getIconPath(deviceType[0], deviceDetail[0])

  modelDevices.append([
    gtk.gdk.pixbuf_new_from_file(iconPath),
    deviceClass, deviceType[1], deviceDetail[1], name, address
  ])

def executeScan():
  "Scan for remote devices"
  devices = btsupport.DeviceDiscoverer()
  devices.newDevice = on_newDevice
  try:
    devices.find_devices(
      lookup_names=settings.get('retrieve names'),
      duration=settings.get('scan duration'),
      flush_cache=settings.get('flush cache')
    )
    readfiles = [ devices, ]
    # Wait till the end
    while True:
      while gtk.events_pending():
        gtk.main_iteration()
      ret = select.select(readfiles, [], [] )[0]
      if devices in ret:
        devices.process_event()
      if devices.done:
        break
  except btsupport.BluetoothError, value:
    print 'bluetooth error: %s' % value

def createDevicesColumns():
  "Create columns for devices list"
  # Column for device icon
  cell = gtk.CellRendererPixbuf()
  column = gtk.TreeViewColumn('')
  column.pack_start(cell)
  column.set_attributes(cell, pixbuf=COL_ICON)
  tvwDevices.append_column(column)

  # Column for device type
  cell = gtk.CellRendererText()
  column = gtk.TreeViewColumn(_('Type'))
  column.pack_start(cell)
  column.set_expand(True)
  column.set_attributes(cell, text=COL_DETAIL)
  tvwDevices.append_column(column)

  # Column for device name
  cell = gtk.CellRendererText()
  column = gtk.TreeViewColumn(_('Device name'))
  column.pack_start(cell)
  column.set_expand(True)
  column.set_attributes(cell, text=COL_NAME)
  tvwDevices.append_column(column)

  # Column for device address
  cell = gtk.CellRendererText()
  column = gtk.TreeViewColumn(_('Bluetooth address'))
  column.pack_start(cell)
  column.set_attributes(cell, text=COL_ADDRESS)
  tvwDevices.append_column(column)

def getIconPath(deviceType, deviceDetail):
  "Return the icon path for a device"
  if deviceType == 0 and deviceDetail == 0:
    # Adapter
    iconPath = handlepaths.getPath('gfx', 'adapter.png')
  else:
    # Normal device
    iconPath = handlepaths.getPath('gfx', 'class%d/%d.png' % (
      deviceType, deviceDetail))
    if not os.path.isfile(iconPath):
      iconPath = handlepaths.getPath('gfx', 'unknown.png')
  return os.path.join(handlepaths.base_path, iconPath)

def on_newDevice(name, address, device_class):
  "Callback function called when a new device has been discovered"
  # Search for the name if not found during scan
  if not name and settings.get('resolve names'):
    # Find name by scanning device
    name = btsupport.getDeviceName(address)
  if name is None:
    name = ''
  addNewDevice(address, name, device_class)

def on_toolbRefresh_clicked(widget, data=None):
  "Detected devices"
  toolbRefresh.set_sensitive(False)
  toolbServices.set_sensitive(False)
  modelDevices.clear()
  executeScan()
  toolbRefresh.set_sensitive(True)

def on_toolbServices_clicked(widget, data=None):
  "Show the details of the selected device"
  iter = tvwDevices.get_selection().get_selected()[1]
  if iter:
    services.showWindow(modelDevices[iter][COL_ADDRESS])

def on_tvwDevices_selection_changed(selection): 
  "Control sensitiveness of the buttons if items are selected"
  model, paths = selection.get_selected_rows()
  toolbServices.set_sensitive(len(paths) > 0)
  btnOk.set_sensitive(len(paths) > 0)

def on_btnOk_clicked(widget, data=None):
  "Search a bound rfcomm port"
  global address
  global portDUN

  iter = tvwDevices.get_selection().get_selected()[1]
  if iter:
    service = services.findDUNService(modelDevices[iter][COL_ADDRESS])
    if service:
      print 'DUN service on %s port %d for device [%s]' % (
        service[1], service[2], modelDevices[iter][COL_ADDRESS])
      address = modelDevices[iter][COL_ADDRESS]
      portDUN = service[2]
    else:
      print 'ERROR: no DUN service found on device [%s]' % \
        modelDevices[iter][COL_ADDRESS]
      dialogError = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, 
        gtk.BUTTONS_OK, _('ERROR: no DUN service found on device [%s]') % \
        modelDevices[iter][COL_ADDRESS])
      dialogError.set_icon_from_file(handlepaths.get_app_logo())
      dialogError.run()
      dialogError.destroy()
      address = None
      portDUN = None

def showWindow():
  "Show devices dialog"
  global tvwDevices
  global modelDevices
  global toolbRefresh
  global toolbServices
  global btnOk
  global address
  global portDUN
  
  gladeFile = gtk.glade.XML(
    fname=handlepaths.getPath('ui', 'devices.glade'),
    domain=handlepaths.APP_NAME)
  # Signals handler
  signals = {
    'on_toolbRefresh_clicked': on_toolbRefresh_clicked,
    'on_toolbServices_clicked': on_toolbServices_clicked,
    'on_btnOk_clicked': on_btnOk_clicked
  }

  gladeFile.signal_autoconnect(signals)
  gw = gladeFile.get_widget
  dialogDevices = gw('dialogDevices')
  dialogDevices.set_icon_from_file(handlepaths.getPath('data', 'cel.svg'))
  dialogDevices.set_title(_('Bluetooth devices'))
  tvwDevices = gw('tvwDevices')
  tvwDevices.get_selection().connect('changed', on_tvwDevices_selection_changed)
  toolbRefresh = gw('toolbRefresh')
  toolbServices = gw('toolbServices')
  btnOk = gw('btnOk')

  # Model for devices (icon, class, type, detail, name, mac, last seen)
  modelDevices = gtk.ListStore(gtk.gdk.Pixbuf, int, str, str, str, str)
  tvwDevices.set_model(modelDevices)
  createDevicesColumns()

  dialogDevices.move(settings.get('window left'), settings.get('window top'))
  dialogDevices.set_default_size(settings.get('window width'), settings.get('window height'))
  dialogDevices.show()
  if settings.get('startup scan'):
    on_toolbRefresh_clicked(None)
  # Repeat dialog until dismissed or a valid rfcomm enabled device has selected
  while True:
    address = None
    portDUN = None
    response = dialogDevices.run()
    if response == gtk.RESPONSE_OK:
      if address and portDUN:
        break
    else:
      break
  # Save window position
  cursize = dialogDevices.get_size()
  curpos = dialogDevices.get_position()
  settings.set('window width', cursize[0])
  settings.set('window height', cursize[1])
  settings.set('window left', curpos[0])
  settings.set('window top', curpos[1])
  dialogDevices.destroy()
  # Save settings on close
  settings.save(clearDefaults=True)
  return address, portDUN
